/*
 * Copyright 2011 The Rusted Roof, LLC.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.proteusframework.runtime;

import org.proteusframework.api.IFactoryRegistrar;
import org.proteusframework.api.IPluginRegistrar;
import org.proteusframework.api.IProteusApplication;
import org.proteusframework.api.IRuntimeEngine;
import org.proteusframework.api.config.IConfigurationLoader;
import org.proteusframework.api.model.IQualifiedName;
import org.proteusframework.api.model.NoSuchQualifiedNameException;
import org.proteusframework.extension.IAdapterManager;
import org.proteusframework.util.Assert;

import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Provides a standard Proteus Framework application context suitable for nearly every imaginable application built
 * atop the Proteus Framework. Generally, an application will use code like this when bootstrapping their application:
 * <p/>
 * <code>
 * IProteusApplication myApplication = new StandardProteusApplication();
 * </code>
 *
 * @author Tacoma Four
 */
public final class StandardProteusApplication implements IProteusApplication
{
    private static final Logger logger = Logger.getLogger(StandardProteusApplication.class.getCanonicalName());

    private IRuntimeEngine engine;
    private IFactoryRegistrar registrar;

    private Map<IQualifiedName, IPluginRegistrar> pluginRegistrarCache = new HashMap<IQualifiedName, IPluginRegistrar>();

    /**
     * {@inheritDoc}
     */
    @Override
    public void setRuntimeEngine(IRuntimeEngine engine)
    {
        Assert.parameterNotNull(engine, "Parameter 'engine' must not be null");

        this.engine = engine;

        if (logger.isLoggable(Level.FINEST))
            logger.finest("Injecting platform delegate into runtime engine");

        this.engine.setPlatformDelegate(this);

        if (logger.isLoggable(Level.FINEST))
            logger.finest("Injecteded platform delegate into runtime engine");

        if (logger.isLoggable(Level.INFO))
            logger.info("A Standard Proteus Application is configured and is now ready to start the application");
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public IAdapterManager getAdapterManager()
    {
        return AdapterManager.getInstance();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public IRuntimeEngine getEngine()
    {
        if (null == engine)
            throw new IllegalStateException("The IRuntimeEngine isn't initiatized and is unavailable at this time");

        return engine;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized IFactoryRegistrar getFactoryRegistrar()
    {
        if (null == registrar)
        {
            registrar = new ProteusRegistrar();
            registrar.setPlatformDelegate(this);
        }

        return registrar;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public IConfigurationLoader createConfigurationLoader()
    {
        return new StandardConfigurationLoader();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public synchronized IPluginRegistrar getPluginRegistrar(IQualifiedName qualifiedName)
            throws NoSuchQualifiedNameException
    {
        IPluginRegistrar pluginRegistrar = null;

        if (pluginRegistrarCache.containsKey(qualifiedName))
        {
            pluginRegistrar = pluginRegistrarCache.get(qualifiedName);
        } else
        {
            if (getFactoryRegistrar().isInstalled(qualifiedName))
            {
                pluginRegistrar = getFactoryRegistrar().createRegistrar(qualifiedName);
                if (null != pluginRegistrar)
                {
                    pluginRegistrarCache.put(qualifiedName, pluginRegistrar);
                }
            } else
            {
                throw new NoSuchQualifiedNameException(qualifiedName);
            }
        }

        return pluginRegistrar;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void startApplication()
    {
        engine.run();
    }
}
